package com.pactera.jep.service.sys.web.controller;

import com.pactera.jep.autoconfigure.web.controller.BaseRestController;
import com.pactera.jep.commons.CollectionUtils;
import com.pactera.jep.commons.DateUtils;
import com.pactera.jep.core.code.PermitType;
import com.pactera.jep.core.context.Context;
import com.pactera.jep.core.security.token.Token;
import com.pactera.jep.core.security.token.TokenService;
import com.pactera.jep.core.security.token.impl.TokenBuilder;
import com.pactera.jep.service.sys.dto.Password;
import com.pactera.jep.service.sys.security.spring.bean.UserEntity;
import com.pactera.jep.service.sys.security.spring.conf.MD5PasswordEncoder;
import com.pactera.jep.service.sys.service.UserProfileService;
import com.pactera.jep.service.sys.util.MD5Encoder;
import com.pactera.jep.sys.code.ResourceKind;
import com.pactera.jep.sys.model.Authorization;
import com.pactera.jep.sys.model.Staff;
import com.pactera.jep.sys.model.User;
import com.pactera.jep.sys.service.*;
import com.pactera.jep.web.code.WebCode;
import com.pactera.jep.web.code.WebHeaderCode;
import com.pactera.jep.web.entity.UserAuthorization;
import io.jsonwebtoken.lang.Assert;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

@RestController
@Api
@RequestMapping("/user")
@SuppressWarnings("rawtypes")
public class UserRestController extends BaseRestController {

    private static final Logger log = LoggerFactory.getLogger(UserRestController.class);

    @Autowired
    UserService userService;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private StaffService staffService;

    @Autowired
    private MenuService menuService;

    @Autowired
    private AuthorizationService authorizationService;

    @Autowired
    private UserProfileService userProfileService;

    @Autowired
    private RoleService roleService;

    @Autowired
    MD5PasswordEncoder passwordEncoder;

    @ApiOperation(value = "获得用户", notes = "使用ID获取用户")
    @GetMapping("/findUserById/{id}")
    public User getUser(
            @ApiParam(name = "id", value = "用户ID", allowEmptyValue = false, required = true) @PathVariable String id,
            @ApiIgnore @RequestHeader(WebHeaderCode.X_USER_TOKEN) String token) {
        log.info(token);
        log.info("staff code is: {}", getStaffCode(token));
        return userService.get(id);
    }

    /**
     * 获取到当前CAS登录的用户
     *
     * @return
     */
    @RequestMapping(value = "/curUser", method = RequestMethod.GET)
    public UserDetails getCurrentCasUser(HttpServletRequest request) {
        // 首先从request中获取auth_info数据
        UserEntity userEntity = new UserEntity();
        String token = request.getHeader("x-user-token");
        String systemId = request.getParameter("systemId");
        if (StringUtils.isBlank(token)) {
            log.error("请求中不带有认证数据!");
            return userEntity;
        } else {
            try {
                String staffCode = getStaffCode(token);
                // 从authInfo中获取到uid
                // 然后调用获取数据的方法查询数据
                userEntity = loadUserDetail(staffCode, systemId);
                return userEntity;
            } catch (Exception e) {
                if (e instanceof UsernameNotFoundException) {
                    log.error("未能找到用户数据!");
                } else {
                    log.error("数据查询出现异常:{}", e.getMessage());
                }
                return userEntity;
            }
        }
    }

    /**
     * 获取到CAS登录的用户
     *
     * @return
     */
    @RequestMapping(value = "/findCasUser/{uid}", method = RequestMethod.GET)
    public UserDetails findCasUser(@PathVariable("uid") String uid) {
        UserEntity userEntity = new UserEntity();
        if (StringUtils.isBlank(uid)) {
            log.error("传入的uid为空！");
            return userEntity;
        }
        try {
            userEntity = loadUserDetail(uid);
            return userEntity;
        } catch (Exception e) {
            if (e instanceof UsernameNotFoundException) {
                log.error("未能查询到用户名为{}的账号.", uid);
            } else {
                log.error("数据查询出现异常:{}", e.getMessage());
            }
            return userEntity;
        }
    }

    private UserEntity loadUserDetail(String username) {
        return loadUserDetail(username, "");
    }


    private UserEntity loadUserDetail(String username, String systemId) {
        if (StringUtils.isEmpty(username)) {
            log.error("传入的用户名为空!");
            throw new UsernameNotFoundException("传入的用户名为空！");
        }
        // 首先通过UserService查询出用户基础数据
        User user = userService.get(username);
        if (user == null) {
            log.error("没有找到用户名为{}的用户数据。", username);
            return null;
        }
        // 如果用户存在，那么从staffService中查询出用户角色
        Staff staff = staffService.get(username);
        if (staff == null) {
            log.error("没有查询到用户名为{}的角色数据。", username);
            throw new UsernameNotFoundException("没有查询到用户" + username + "的角色!");
        }
        // 获取数据权限
        List<Authorization> authorizations = authorizationService
                .selectPermitAuthorization4StaffCode(staff.getStaffCode());
        Map<String, List<UserAuthorization>> authMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(authorizations)) {
            for (Authorization auth : authorizations) {
                if (!ResourceKind.PERMIT.name().equals(auth.getKind()))
                    continue;

                UserAuthorization userAuth = new UserAuthorization();
                userAuth.setKind(ResourceKind.PERMIT.name());
                /* 数据权限类型 */
                PermitType type = PermitType.valueOf(auth.getResourceCode());
                userAuth.setResourceCode(type.name());
                userAuth.setResourceData(type.getWeight());

                String menuCode = auth.getMenuCode();
                List<UserAuthorization> list = authMap.get(menuCode);
                if (list == null) {
                    list = new ArrayList<>();
                    authMap.put(menuCode, list);
                }
                list.add(userAuth);
            }
        }
        // 创建token
        TokenBuilder builder = TokenBuilder.newTokenBuilder(user.getUserId(), "login", "login")
                .username(user.getUserName()).property("loginTime", DateUtils.formatDateTime(new Date()))
                .property("unitCode", staff.getUnitCode()).property("unitName", staff.getUnitName())
                .property("deptCode", staff.getDeptCode()).property("deptName", staff.getDeptName())
                .property("tenantId", staff.getTenantId()).property("authorization", authMap);

        Token loginToken = builder.expirationInMinutes(30).build();
        String newToken = tokenService.encode(loginToken);
        UserEntity userEntity = new UserEntity(user);
        userEntity.setToken(newToken);
        userEntity.setStaff(staff);
        userEntity.setMenus(menuService.selectMenu(staff.getStaffCode(), systemId));
        userEntity.setRoles(roleService.selectRoleCodeByStaffCode(staff.getStaffCode()));
        return userEntity;
    }

    @ApiOperation(value = "验证提交的用户密码是否与原密码一致")
    @GetMapping(value = "/checkOldPass")
    public Map<String, Object> checkOldPass(User user) {
        Map<String, Object> tips = new HashMap<String, Object>();
        boolean exist = userProfileService.checkOldPass(user);
        if (exist) {
            tips.put("valid", true);
        } else {
            tips.put("valid", false);
        }
        return tips;
    }

    @ApiOperation(value = "修改用户密码")
    @PostMapping(value = "/savePass")
    public Map<String, Object> savePass(@RequestBody Password password) {
        Assert.hasLength(password.getNewPassword(), "新密码为空");
        Assert.hasLength(password.getPassword(), "旧密码为空");
        //AES解密密码
        password.decode();
        Assert.isTrue(password.getNewPassword().equals(password.getRepeatPassword()), "新密码两次输入不一致");
        Assert.isTrue(!password.getNewPassword().equals(password.getPassword()), "新旧密码不能相同");
        Assert.isTrue(pwdVerify(password.getNewPassword()), "新密码强度不够, 至少包含数字、小写字母、大写字母、特殊字符中的三种");

        Map<String, Object> tips = new HashMap<String, Object>();
        String userId = Context.getIdentityContext().getIdentity().getId();
        User u = userService.get(userId);
        String enPassword = MD5Encoder.encode(password.getPassword(), u.getUserId(), 1);
        Assert.isTrue(enPassword.equals(u.getPassword()), "旧密码不匹配");
        u.setPassword(MD5Encoder.encode(password.getNewPassword(), u.getUserId(), 1));
        userService.update(u);
        tips.put(WebCode.SUCCESS, true);
        tips.put(WebCode.MSG, WebCode.SUCCESS_SAVE);

        return tips;
    }

    /**
     * 密码至少包含数字、小写字母、大写字母、特殊字符中的三种
     */
    private boolean pwdVerify(String pwd) {
        int flag = 0;
        if (pwd.matches(".*[0-9].*")) {
            flag++;
        }
        if (pwd.matches(".*[a-z].*")) {
            flag++;
        }
        if (pwd.matches(".*[A-Z].*")) {
            flag++;
        }
        if (pwd.matches(".*[!@*#$\\-_()+=&￥].*")) {
            flag++;
        }
        return flag >= 3;
    }

    @ApiOperation(value = "重置密码")
    @PostMapping(value = "/resetPassword")
    public Map<String, Object> resetPassword(@RequestBody Staff staff) {
        Map<String, Object> tips = new HashMap<String, Object>();
        try {
            User u = userService.get(staff.getStaffCode());
            u.setPassword(MD5Encoder.encode("smartcl@2020", u.getUserId(), 1));
            userService.update(u);
            tips.put(WebCode.SUCCESS, true);
            tips.put(WebCode.MSG, WebCode.SUCCESS_SAVE);
        } catch (Exception e) {
            log.info(e.getMessage());
            tips.put(WebCode.SUCCESS, false);
            tips.put(WebCode.MSG, WebCode.FAILURE_SAVE);
        }
        return tips;
    }

}
